import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;

@SuppressWarnings("serial")
public class ScheduleGenerator implements Serializable{
	static String PLAYER_NAME_PREFIX = "Player";
	
	private String competitionDt;
	private String competitionName;
	private int issuingPlayerId = 0;
	private int issuingMatchId = 0;
	private int recursionPlayerId = 0;
	MatchNode rootNode;
	
	int numOfPlayer;
	ArrayList<Player> playerList;
	ArrayList<Player> playerOrderedList;
	ArrayList<Match> matchList;

	public ScheduleGenerator(String gameDt, String gameName) {
		this.competitionDt = gameDt;
		this.competitionName = gameName;
		playerList = new ArrayList<Player>();
		playerOrderedList = new ArrayList<Player>();
		matchList = new ArrayList<Match>();
	}
	
	public void shufflePlayer() {
		Collections.shuffle(playerList);
	}
	
	public MatchNode generateSchedule() {
		rootNode = calcScheduleRecursion(numOfPlayer);
		rootNode.calcDepth(rootNode.getDepth());
		
		return rootNode;
	}
	
	private MatchNode calcScheduleRecursion(int nodeCnt) {
		if(nodeCnt == 1) {
			return playerList.get(recursionPlayerId++);
		}
		else {
			int rightNodeNum = (int)(nodeCnt / 2); 
			int leftNodeNum = nodeCnt - rightNodeNum;
			MatchNode left = this.calcScheduleRecursion(leftNodeNum);
			MatchNode right = this.calcScheduleRecursion(rightNodeNum);
			
			Match currMatch = new Match(issueMatchId(),left, right);
			matchList.add(currMatch);
			return currMatch;
		}		
	}
	
	private int issuePlayerId() {
		return issuingPlayerId++;
	}
	
	private int issueMatchId() {
		return issuingMatchId++;
	}
	
	public void setPlayer(int num) {
		String[] nameList = new String[num];
		
		for(int i=0;i<nameList.length;i++) {
			nameList[i] = PLAYER_NAME_PREFIX + (i + 1);
		}
		
		setPlayer(nameList);
	}
	
	public void setPlayer(String[] nameList) {
		numOfPlayer = nameList.length;

		for(int i=0;i<numOfPlayer;i++) {
			Player currPlayer = new Player(issuePlayerId(),nameList[i]);
			playerList.add(currPlayer);
			playerOrderedList.add(currPlayer);
		}
	}
	
	public void setPlayer(String[][] nameLevelList) {
		numOfPlayer = nameLevelList.length;

		for(int i=0;i<numOfPlayer;i++) {
			String[] currPos = nameLevelList[i];
			
			Player currPlayer = new Player(issuePlayerId(), currPos[0], Integer.parseInt(currPos[1]));
			playerList.add(currPlayer);
			playerOrderedList.add(currPlayer);
		}
	}
	
	public String drawText() {
		return drawText(DrawLevel.DRAW_NAME);
	}
	
	public String drawText(DrawLevel lv) {
		String result = getCompetitionInfo();
		result += Match.CONST_LF;
		result += Match.CONST_LF;
		

		result += "** Tournament Schedule";
		result += Match.CONST_LF;
		result += Match.CONST_LF;
		result += getScheduleHeader();
		result += Match.CONST_LF;
		result += Match.CONST_LF;
		result += getScheduleInfo(lv); 
		result += Match.CONST_LF;
		result += Match.CONST_LF;
		
		result += "** Players Information";
		result += Match.CONST_LF;
		result += Match.CONST_LF;
		result += getAllPlayersInfo(lv); 
		result += Match.CONST_LF;
		result += Match.CONST_LF;
		
		result += "** Matches Information";
		result += Match.CONST_LF;
		result += Match.CONST_LF;
		result += getAllMatchesInfo(lv); 
		result += Match.CONST_LF;
		result += Match.CONST_LF;
		
		return result;
	}
	
	private String getCompetitionInfo() {
		return "[ " + competitionDt + ", " + competitionName + ", Num of Player : " + numOfPlayer + " ]";
	}
	
	private String getScheduleHeader() {
		String header = "";
		int maxDepth = getRootNode().getDepth();
		
		for(int i=0;i<maxDepth;i++) {
			header += String.format("%-20s","     - Round " + (i+1) + " -");
		}
		
		return header;
	}
	
	private String getScheduleInfo(DrawLevel lv) {
		return rootNode.toString(lv);
	}
	
	private String getAllPlayersInfo(DrawLevel lv) {
		String result = "";
		for(int i=0;i<playerOrderedList.size();i++) {
			result += playerOrderedList.get(i).getInfo(lv) + Match.CONST_LF;
		}
		return result;
	}
	
	public String getAllMatchesInfo(DrawLevel lv) {
		String result = "";
		for(int i=0;i<matchList.size();i++) {
			result += matchList.get(i).getInfo(lv) + Match.CONST_LF;
		}
		return result;		
	}
	
	public Match getMatchNode(int i) {
		return matchList.get(i);
	}
	
	public MatchNode getRootNode() {
		return rootNode;
	}
}
